package com.why.simpleboot.core.server.netty;

import com.why.simpleboot.core.common.SimpleBootBanner;
import com.why.simpleboot.core.configration.ConfigurationFactory;
import com.why.simpleboot.core.configration.SimpleBootConfiguration;
import com.why.simpleboot.core.core.ApplicationContext;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @program: why-simpleboot-framework
 * @description:
 * @author: @why
 * @create: 2021-05-22 14:06
 **/
public class SimpleBootHttpServer {
    private int port;
    private Logger log = LoggerFactory.getLogger(SimpleBootHttpServer.class);
    private static final EventLoopGroup WORKER = new NioEventLoopGroup();
    private static final EventLoopGroup BOSS = new NioEventLoopGroup(1, new DefaultThreadFactory("boss"));

    /***
     * 持有一个服务启动类
     */
    private static final ServerBootstrap SERVER_BOOTSTRAP = new ServerBootstrap();

    public SimpleBootHttpServer() {

    }

    public SimpleBootHttpServer(int port) {
        this.port = port;
    }

    public void serverStart(ApplicationContext applicationContext, String... startPosition) {
        SimpleBootConfiguration config = ConfigurationFactory.getConfig();
        Integer portProperties = config.getInt("port");
        if (portProperties != null) {
            port = portProperties;

        } else {
            port=6666;
            log.info("netty use default port to starting server:{}", 6666);
        }


        log.info("netty开始启动。。。");
        SimpleBootBanner.printBanner(System.out);
        try {
            SERVER_BOOTSTRAP
                    .group(BOSS, WORKER)
                    .channel(NioServerSocketChannel.class)
                    //TCP_NODELAY用于启用或关于Nagle算法。如果要求高实时性，
                    // 有数据发送时就马上发送，就将该选项设置为true关闭Nagle算法；
                    // 如果要减少发送次数减少网络交互，就设置为false等累积一定大小后再发送。默认为false。
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    //启用心跳保活机制
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //允许重复使用本地地址和端口
                    .childOption(ChannelOption.SO_REUSEADDR, true)
                    //用于临时存放已完成三次握手的请求的队列的最大长度
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            //HttpRequestDecoder和HttpResponseEncoder分别用于对请求进行解码、对响应进行编码，
                            // 为了方便也可以使用HttpServerCodec编解码器来代替HttpRequestDecoder和HttpResponseEncoder
                            pipeline.addLast(new HttpServerCodec());
                            pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024));
                            pipeline.addLast(new HttpRequestHandler());
                        }
                    });
            ///bind()是异步的通过 sync()方法将其变为同步。
            ChannelFuture future = SERVER_BOOTSTRAP.bind(port).sync();
            log.info("服务器启动完成！端口号为:{}", port);
//执行那些实现runner接口的类。  留一个口子  能够操作运行时候的容器
           new Thread(()->{ applicationContext.startRunner(startPosition); }).start();

//            不能挪出来，挪出来就执行finally代码块了
            future.channel().closeFuture().sync();
            future.addListener(ChannelFutureListener.CLOSE);

        } catch (InterruptedException e) {
            log.error("绑定端口号失败。。。", e);
        } finally {
            BOSS.shutdownGracefully();
            WORKER.shutdownGracefully();
        }


    }


    public void setPort(int port) {
        this.port = port;
    }


}
